package com.netsdk.utils;


import com.netsdk.config.LoginMes;
import com.netsdk.entity.JsonData;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.NetSDKLib.LLong;
import com.netsdk.lib.ToolKits;
import com.netsdk.lib.enumeration.ENUMERROR;
import com.netsdk.lib.enumeration.NET_EM_FAILCODE;
import com.sun.jna.Memory;
import com.sun.jna.Pointer;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import org.apache.commons.io.IOUtils;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URL;
import java.net.URLConnection;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author lgj
 * @version 1.0
 * @description 设备调用sdk基本功能；
 * @date 2022/5/11
 */
public class DeviceModule {
    public static NetSDKLib netSdk = NetSDKLib.NETSDK_INSTANCE;
    public static NetSDKLib netConfig = NetSDKLib.CONFIG_INSTANCE;

    //登录句柄(用来匹配设备)
    public static LLong loginHandler = new LLong(0);

    private static boolean bInit = false;
    private static boolean bLogopen = false;

    //超时时间
    private final static int TIME_OUT = 6 * 1000;

    //初始化sdk
    public static boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) {

        //初始化返回值
        bInit = netSdk.CLIENT_Init(disConnect, null);
        //判断初始化是否成功
        if (!bInit) {
            System.out.println("初始化失败");
            return false;
        }

        //打开日志
        NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
        File path = new File("../sdk.log");
        if (!path.exists()) {
            path.mkdir();
        }
        String logPath = path.getAbsoluteFile().getParent() + "\\sdklog" + ToolKits.getDate() + ".log";
        setLog.nPrintStrategy = 0;
        setLog.bSetFilePath = 1;
        System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
        System.out.println(logPath);
        setLog.bSetPrintStrategy = 1;
        bLogopen = netSdk.CLIENT_LogOpen(setLog);
        if (!bLogopen) {
            System.out.println("SDK日志打印失败");
        }

        //设置重连回调接口，当设备出现断线情况，sdk内部会自动进行冲凉操作
        //可选操作，建议设置
        netSdk.CLIENT_SetAutoReconnect(haveReConnect, null);

        //设置登录超时时间和尝试次数，可选
        int waitTime = 5000;//登录请求响应超时时间设置为5秒
        int tryTimes = 1;//登录时尝试建立连接1次
        netSdk.CLIENT_SetConnectTime(waitTime, tryTimes);

        // 设置更多网络参数，NET_PARAM的nWaittime，nConnectTryNum成员与CLIENT_SetConnectTime
        // 接口设置的登录设备超时时间和尝试次数意义相同,可选
        NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM();
        netParam.nConnectTime = 10000;      // 登录时尝试建立链接的超时时间
        netParam.nGetConnInfoTime = 3000;   // 设置子连接的超时时间
        netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间，为0默认1000ms
        netSdk.CLIENT_SetNetworkParam(netParam);

        return true;
    }

    //清除环境
    public static void cleanup() {
        //先关掉日志
        if (bInit) {
            netSdk.CLIENT_LogClose();
        }
        //再清除设备开启的业务
        if (bInit) {
            netSdk.CLIENT_Cleanup();
        }
    }

    //设备信息
    public static NetSDKLib.NET_DEVICEINFO_Ex m_setDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();

    //登录设备
    public static boolean login(String m_strIp, int m_port, String m_strUser, String m_strPassword) {

        //登录句柄(用来匹配设备)
        //入参
        NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
        pstInParam.szIP = m_strIp.getBytes();
        pstInParam.nPort = m_port;
        pstInParam.szUserName = m_strUser.getBytes();
        pstInParam.szPassword = m_strPassword.getBytes();
        pstInParam.emSpecCap = 0;
        //出参
        NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
        //高安全级别登录
        loginHandler = netSdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
        //保存本地设备登陆id
        LoginMes.loginMap.put("bendi",loginHandler);

        if (loginHandler.longValue() == 0) {
            System.err.println("登录失败");
            System.err.printf("login Device[%s] Port[%d]Failed. %s\n",
                    m_strIp, m_port, ToolKits.getErrorCodePrint());
            return false;
        } else {
            System.out.println("登陆成功");
            return true;
        }
    }

    //登出
    public static boolean logout(String serialNumber) {
        LLong loginId = LoginMes.loginMap.get(serialNumber);
        //未登录状态不需要登出
        if (loginId.longValue() == 0) {
            return false;
        }
        boolean bRet = netSdk.CLIENT_Logout(loginId);
        if (bRet) {
            LoginMes.loginMap.put(serialNumber,new LLong(0));
        }
        return bRet;
    }

    //0下发人员,1更新人员
    public static JsonData updatePerson(Map<String,Object> paramMap,int type) {
        String serialNumber = String.valueOf(paramMap.get("serialNumber"));
        List<Map<String,String>> userList= (List<Map<String,String>>)paramMap.get("jsonData");
        //入参用户信息
        NetSDKLib.NET_IN_ACCESS_USER_SERVICE_INSERT userInsert = new NetSDKLib.NET_IN_ACCESS_USER_SERVICE_INSERT();
        //添加的用户数量
        userInsert.nInfoNum = userList.size();
        /**
         * 必要参数,用户ID,用户姓名,用户类型,是否首用户,身份证号,卡+密码开门时的密码,门权限,门权限对应时间段
         */
        NetSDKLib.NET_ACCESS_USER_INFO[] infos = new NetSDKLib.NET_ACCESS_USER_INFO[userInsert.nInfoNum];
        NetSDKLib.FAIL_CODE[] failCodes = new NetSDKLib.FAIL_CODE[userInsert.nInfoNum];
        for (int i = 0; i < infos.length; i++) {
            infos[i] = new NetSDKLib.NET_ACCESS_USER_INFO();
            failCodes[i] = new NetSDKLib.FAIL_CODE();
        }
//
//        List<String[]> list = new ArrayList<String[]>();
//        list.add(new String[]{userId, userName, password});
        for (int i = 0; i < userInsert.nInfoNum; i++) {
            //赋值用户id
            System.arraycopy(userList.get(i).get("userId").getBytes(), 0, infos[i].szUserID, 0, userList.get(i).get("userId").getBytes().length);
            //用户名
            try {
                //linux环境下编码格式为UTF-8,windows下为GBK
                System.arraycopy(userList.get(i).get("userName").getBytes("UTF-8"), 0, infos[i].szName, 0, userList.get(i).get("userName").getBytes("UTF-8").length);
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
            //密码
            System.arraycopy(userList.get(i).get("password").getBytes(), 0, infos[i].szPsw, 0, userList.get(i).get("password").getBytes().length);
            // 用户类型
            infos[i].emUserType = Integer.valueOf(userList.get(i).get("userType"));
            // 来宾卡的通行次数
//            infos[i].nUserTime = Integer.valueOf(userList.get(i).get("userTime"));
            infos[i].nUserTime = 100;
//            //是否首用户
//            infos[i].bFirstEnter = 1;
//            infos[i].nFirstEnterDoorsNum = 1;
//            infos[i].nFirstEnterDoors[0] = 0;
            //身份证号
//            String numbers = "1232222222";
//            System.arraycopy(numbers.getBytes(), 0, infos[i].szCitizenIDNo, 0, numbers.getBytes().length);
            // 有效门数, 门个数 表示双门控制器(默认1)
//            infos[i].nDoorNum = Integer.valueOf(userList.get(i).get("doorNum"));
            infos[i].nDoorNum = 1;
            // 有权限的门序号, 表示第一个门有权限（默认0）
//            infos[i].nDoors[0] = Integer.valueOf(userList.get(i).get("doorNum"));
            infos[i].nDoors[0] = 0;
            // 房间个数
            infos[i].nRoom = 0;
            /*// 房间号
            System.arraycopy(list.get(i)[3].getBytes(), 0,
                    infos[i].szRoomNos[0].szRoomNo, 0,
                    list.get(i)[3].getBytes().length);*/
            // 与门数对应
            infos[i].nTimeSectionNum = 1;
            // 表示第一个门全天有效
            infos[i].nTimeSectionNo[0] = 255;
            // 开始有效期
            String sDate = userList.get(i).get("sDate");
            String[] sDates=sDate.split("-");
            infos[i].stuValidBeginTime.setTime(Integer.valueOf(sDates[0]), Integer.valueOf(sDates[1]), Integer.valueOf(sDates[2]), 0, 0, 0);
            // 结束有效期
            String eDate = userList.get(i).get("eDate");
            String[] eDates = eDate.split("-");
            infos[i].stuValidEndTime.setTime(Integer.valueOf(eDates[0]), Integer.valueOf(eDates[1]), Integer.valueOf(eDates[2]), 23, 59, 59);
        }

        /**
         * 指针分配内存
         */
        userInsert.pUserInfo = new Memory(infos[0].size() * userInsert.nInfoNum);
        /**
         * 数据写入指针
         */
        ToolKits.SetStructArrToPointerData(infos, userInsert.pUserInfo);
        /**
         * 入参分配内存
         */
        Pointer inParam = new Memory(userInsert.size());
        ToolKits.SetStructDataToPointer(userInsert, inParam, 0);
        /**
         * 出参分配内存
         */
        NetSDKLib.NET_OUT_ACCESS_USER_SERVICE_INSERT userInsertOut = new NetSDKLib.NET_OUT_ACCESS_USER_SERVICE_INSERT();
        userInsertOut.nMaxRetNum = userInsert.nInfoNum;
        for (int i = 0; i < failCodes.length; i++) {
            failCodes[i] = new NetSDKLib.FAIL_CODE();
        }
        userInsertOut.pFailCode = new Memory(failCodes[0].size() * userInsertOut.nMaxRetNum); // 申请内存
        userInsertOut.pFailCode.clear(failCodes[0].size() * userInsertOut.nMaxRetNum);
        ToolKits.SetStructArrToPointerData(failCodes, userInsertOut.pFailCode);
        userInsertOut.write();
        boolean isAdd = netSdk.CLIENT_OperateAccessUserService(new NetSDKLib.LLong(LoginMes.loginMap.get(serialNumber).longValue()), NetSDKLib.NET_EM_ACCESS_CTL_USER_SERVICE.NET_EM_ACCESS_CTL_USER_SERVICE_INSERT, inParam, userInsertOut.getPointer(), 5000);
        if (isAdd) {
            if(type==0){
                return JsonData.buildSuccess("添加人员成功");
            }else{
                return JsonData.buildSuccess("修改人员成功");
            }
        } else {
            // 将指针转为具体的信息
            ToolKits.GetPointerDataToStructArr(userInsertOut.pFailCode, failCodes);
            /**
             * 打印错误信息
             */
            String failStr = "";
            for (int i = 0; i < userInsertOut.nMaxRetNum; i++) {
                System.out.println("[" + i + "]添加用户结果："
                        + failCodes[i].nFailCode);
                failStr += userList.get(i).get("userId")+":"+NET_EM_FAILCODE.FailCodeMessage(failCodes[i].nFailCode)+";";
            }
            if(type==0){
                return JsonData.buildError("添加人员失败," + ENUMERROR.getErrorMessage() +"sdk错误码："+ENUMERROR.getErrorCode()+"添加结果："+failStr);
            }else {
                return JsonData.buildError("修改人员失败," + ENUMERROR.getErrorMessage() +"sdk错误码："+ENUMERROR.getErrorCode()+"修改结果："+failStr);
            }
        }
    }

    //获取网络图片字节流
    public static byte[] getUrlPicByte(String url) {
        InputStream in = null;
        // 网络地址
        URL urlObj = null;
        try {
            urlObj = new URL(url);
            URLConnection urlConnection = urlObj.openConnection();
            urlConnection.setConnectTimeout(30 * 1000);
            urlConnection.setReadTimeout(60 * 1000);
            urlConnection.setDoInput(true);
            in = urlConnection.getInputStream();
            byte[] bytes = IOUtils.toByteArray(in);
            return bytes;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }


    /**
     * 下发人脸
     */
    public static JsonData addFace(Map<String,Object> paramMap) {
        String serialNumber = String.valueOf(paramMap.get("serialNumber"));
        List<Map<String,String>> userList= (List<Map<String,String>>)paramMap.get("jsonData");
        // 添加人脸的用户最大个数
        int nMaxCount = userList.size();

        // ////////////////////// 每个用户的人脸信息初始化 ////////////////////////
        NetSDKLib.NET_ACCESS_FACE_INFO[] faces = new NetSDKLib.NET_ACCESS_FACE_INFO[nMaxCount];
        for (int i = 0; i < faces.length; i++) {
            faces[i] = new NetSDKLib.NET_ACCESS_FACE_INFO();

            faces[i].nInFacePhotoLen[0] = 200 * 1024;
            faces[i].pFacePhotos[0].pFacePhoto = new Memory(200 * 1024); // 人脸照片数据,大小不超过200K
            faces[i].pFacePhotos[0].pFacePhoto.clear(200 * 1024);
        }

        // ////////////////////////////// 人脸信息赋值 ///////////////////////////////
        for (int i = 0; i < faces.length; i++) {
            // 用户ID
            System.arraycopy(userList.get(i).get("userId").getBytes(), 0,
                    faces[i].szUserID, 0, userList.get(i).get("userId").getBytes().length);

            // 人脸照片个数
            faces[i].nFacePhoto = 1;

            // 图片数据，目前一个用户ID只支持添加一张
            byte[] szFacePhotoData = getUrlPicByte(userList.get(i).get("picUrl"));

            // 每张照片实际大小
            faces[i].nOutFacePhotoLen[0] = szFacePhotoData.length;

            // 图片数据
            faces[i].pFacePhotos[0].pFacePhoto.write(0,
                    szFacePhotoData, 0,
                    szFacePhotoData.length);
        }
        // ///////////////////////////////////////////////////////////////////////

        // 初始化
        NetSDKLib.FAIL_CODE[] failCodes = new NetSDKLib.FAIL_CODE[nMaxCount];
        for (int i = 0; i < failCodes.length; i++) {
            failCodes[i] = new NetSDKLib.FAIL_CODE();
        }

        // 人脸操作类型
        // 添加人脸信息
        int emtype = NetSDKLib.NET_EM_ACCESS_CTL_FACE_SERVICE.NET_EM_ACCESS_CTL_FACE_SERVICE_INSERT;

        /**
         * 入参
         */
        NetSDKLib.NET_IN_ACCESS_FACE_SERVICE_INSERT stIn = new NetSDKLib.NET_IN_ACCESS_FACE_SERVICE_INSERT();
        stIn.nFaceInfoNum = nMaxCount;
        stIn.pFaceInfo = new Memory(faces[0].size() * nMaxCount);
        stIn.pFaceInfo.clear(faces[0].size() * nMaxCount);

        ToolKits.SetStructArrToPointerData(faces, stIn.pFaceInfo);

        /**
         * 出参
         */
        NetSDKLib.NET_OUT_ACCESS_FACE_SERVICE_INSERT stOut = new NetSDKLib.NET_OUT_ACCESS_FACE_SERVICE_INSERT();
        stOut.nMaxRetNum = nMaxCount;
        stOut.pFailCode = new Memory(failCodes[0].size() * nMaxCount);
        stOut.pFailCode.clear(failCodes[0].size() * nMaxCount);

        ToolKits.SetStructArrToPointerData(failCodes, stOut.pFailCode);

        stIn.write();
        stOut.write();
        boolean isSuccess= netSdk.CLIENT_OperateAccessFaceService(new NetSDKLib.LLong(LoginMes.loginMap.get(serialNumber).longValue()), emtype,
                stIn.getPointer(), stOut.getPointer(), 5000);
        String res = "";
        if (isSuccess) {
            res = "添加成功"+nMaxCount+"张人脸照片";
        } else {
            // 将获取到的结果信息转成具体的结构体
            ToolKits.GetPointerDataToStructArr(stOut.pFailCode, failCodes);
            // 打印具体信息
            String failStr = "";
            for (int i = 0; i < nMaxCount; i++) {
                System.out.println("[" + i + "]添加人脸结果 : "
                        + failCodes[i].nFailCode);
                failStr+= "用户"+userList.get(i).get("userId")+"下发结果："+NET_EM_FAILCODE.FailCodeMessage(failCodes[i].nFailCode)+"; ";
            }
            res="添加人脸失败:" + ENUMERROR.getErrorMessage()+";sdk错误码："+ENUMERROR.getErrorCode()+";人脸下发结果："+failStr;
        }
        stIn.read();
        stOut.read();
        if(isSuccess){
            return JsonData.buildSuccess(res);
        }else {
            return JsonData.buildError(res);
        }
    }

    //删除人脸
    public static boolean deleteFace(Map<String,Object> paramMap){
        String serialNumber = String.valueOf(paramMap.get("serialNumber"));
        List<Map<String,String>> userList= (List<Map<String,String>>)paramMap.get("jsonData");
        int userCount = userList.size();
        //记录每条记录出错问题
        NetSDKLib.FAIL_CODE[] failCodes = new NetSDKLib.FAIL_CODE[userCount];
        for(int i=0;i<userCount;i++){
            failCodes[i] = new NetSDKLib.FAIL_CODE();
        }

        //设置操作人员的类型
        int type = NetSDKLib.NET_EM_ACCESS_CTL_FACE_SERVICE.NET_EM_ACCESS_CTL_FACE_SERVICE_REMOVE;

        //入参
        NetSDKLib.NET_IN_ACCESS_FACE_SERVICE_REMOVE stIn = new NetSDKLib.NET_IN_ACCESS_FACE_SERVICE_REMOVE();
        stIn.nUserNum = userCount;
        for(int i = 0;i<userCount;i++){
            System.arraycopy(userList.get(i).get("userId").getBytes(),0,stIn.szUserIDs[i].szUserID,0,userList.get(i).get("userId").getBytes().length);
        }

        //出参
        NetSDKLib.NET_OUT_ACCESS_FACE_SERVICE_REMOVE stOut = new NetSDKLib.NET_OUT_ACCESS_FACE_SERVICE_REMOVE();
        stOut.nMaxRetNum = userCount;
        stOut.pFailCode = new Memory(failCodes[0].size()*userCount);
        stOut.pFailCode.clear(failCodes[0].size()*userCount);

        //将结构体拷贝到内存
        ToolKits.SetStructArrToPointerData(failCodes,stOut.pFailCode);
        stOut.write();
        stIn.write();
        boolean isSuccess = netSdk.CLIENT_OperateAccessFaceService(LoginMes.loginMap.get(serialNumber),type,stIn.getPointer(), stOut.getPointer(), TIME_OUT);
        stIn.read();
        stOut.read();
        return isSuccess;
    }


    //删除人员
    public static JsonData deletePerson(Map<String,Object> paramMap){
        String serialNumber = String.valueOf(paramMap.get("serialNumber"));
        List<Map<String,String>> userList= (List<Map<String,String>>)paramMap.get("jsonData");
        int userCount = userList.size();
        //创建每条记录出错问题
        NetSDKLib.FAIL_CODE[] failCodes = new NetSDKLib.FAIL_CODE[userCount];
        for(int i=0;i<userCount;i++){
            failCodes[i] = new NetSDKLib.FAIL_CODE();
        }

        //设置操作人员的类型
        int type = NetSDKLib.NET_EM_ACCESS_CTL_USER_SERVICE.NET_EM_ACCESS_CTL_USER_SERVICE_REMOVE;

        //入参
        NetSDKLib.NET_IN_ACCESS_USER_SERVICE_REMOVE stIn = new NetSDKLib.NET_IN_ACCESS_USER_SERVICE_REMOVE();
        stIn.nUserNum = userCount;
        for(int i = 0;i<userCount;i++){
            System.arraycopy(userList.get(i).get("userId").getBytes(),0,stIn.szUserIDs[i].szUserID,0,userList.get(i).get("userId").getBytes().length);
        }

        //出参
        NetSDKLib.NET_OUT_ACCESS_USER_SERVICE_REMOVE stOut = new NetSDKLib.NET_OUT_ACCESS_USER_SERVICE_REMOVE();
        stOut.nMaxRetNum = userCount;
        stOut.pFailCode = new Memory(failCodes[0].size()*userCount);
        stOut.pFailCode.clear(failCodes[0].size()*userCount);

        //将结构体拷贝到内存
        ToolKits.SetStructArrToPointerData(failCodes,stOut.pFailCode);
        stOut.write();
        stIn.write();
        boolean isSuccess = netSdk.CLIENT_OperateAccessUserService(LoginMes.loginMap.get(serialNumber),type,stIn.getPointer(), stOut.getPointer(), TIME_OUT);
        String resStr = "";
        if(isSuccess){
            // 将获取到的结果信息转成具体的结构体
            ToolKits.GetPointerDataToStructArr(stOut.pFailCode, failCodes);
//            for (int i = 0; i < userCount; i++) {
//                System.out.println("[" + i + "]删除人脸结果 : "
//                        + failCodes[i].nFailCode);
//                resStr += "用户"+list.get(i).get("userId")+"结果："+NET_EM_FAILCODE.FailCodeMessage(failCodes[i].nFailCode);
//            }
            resStr = "删除成功";
        }else {
            // 打印具体信息
            String strFail = "";
            for (int i = 0; i < userCount; i++) {
                System.out.println("[" + i + "]删除人脸结果 : "
                        + failCodes[i].nFailCode);
                strFail += "用户"+userList.get(i).get("userId")+"结果："+NET_EM_FAILCODE.FailCodeMessage(failCodes[i].nFailCode);
            }
            System.out.println("删除失败");
            resStr = "删除失败,错误信息："+strFail;
        }
        stIn.read();
        stOut.read();
        if(isSuccess){
            //成功
            return JsonData.buildSuccess(resStr);
        }else {
            //失败
            return JsonData.buildError(resStr);
        }
    }

    /**
     * 开门
     * @return
     */
    public static JsonData openDoor(String serialNumber) {
        NetSDKLib.NET_CTRL_ACCESS_OPEN open = new NetSDKLib.NET_CTRL_ACCESS_OPEN();
        open.nChannelID = 0;
        open.write();
        String openStr = "";
        boolean openSuccess = netSdk.CLIENT_ControlDeviceEx(LoginMes.loginMap.get(serialNumber), NetSDKLib.CtrlType.CTRLTYPE_CTRL_ACCESS_OPEN, open.getPointer(), null, 3000);
        if (!openSuccess) {
            openStr = "开门失败，sdk错误码："+ENUMERROR.getErrorCode()+"错误信息"+ENUMERROR.getErrorMessage();
            return JsonData.buildError("开门失败，sdk错误码："+ENUMERROR.getErrorCode()+"错误信息"+ENUMERROR.getErrorMessage());
        }else {
            openStr = "开门成功";
        }
        open.read();
        if(!openSuccess){
            return JsonData.buildError(openStr);
        }else{
            return JsonData.buildSuccess(openStr);
        }
    }

}
